/*
 * date: 2009-11-23 
 * author: Maciej Smolenski <jezdnia@gmail.com>
 */
#include <signal.h>
#include <sstream>
#include "config.hpp"
#include "log.hpp"
#include "rpc_config.hpp"
#include "rpc_monitor.hpp"
#include "rpc_portmap_query.hpp"
#include "rpc_error.hpp"
#include "rpc_connection.hpp"
#include "boost/interprocess/creation_tags.hpp"

namespace rc=rpc_config;
namespace rm=rpc_monitor;
namespace rq=rpc_query;
namespace re=rpc_error;
namespace rconn=rpc_connection;

#define LOG_IDENT "rpcm_server"

rc::rpc_config_service<boost::interprocess::create_only_t> config(SHARE_CONFIG_NAME, SHARE_CONFIG_SIZE, CONFIG_MUTEX_NAME);
rm::rpc_monitor_service<boost::interprocess::create_only_t> monitor(SHARE_MONITOR_NAME, SHARE_MONITOR_SIZE, MONITOR_MUTEX_NAME);

bool finish = false;

void finish_handler(int signum, siginfo_t *sig_info, void *context)
{
	finish = true;
}

int service()
{
	long portmap_timeout;
	long ping_timeout;	
	rc::stdset hosts;
	if (config.get_hosts(hosts)==result_failure)
		SYSLOG_RETURN(result_failure, "config.get_hosts failed");

	if (config.get_portmap_timeout(portmap_timeout)==result_failure)
		SYSLOG_RETURN(result_failure, "config.get_portmap_timeout failed");

	if (config.get_ping_timeout(ping_timeout)==result_failure)
		SYSLOG_RETURN(result_failure, "config.get_ping_timeout failed");

	rm::stdset all_services;

	for(rc::stdset::const_iterator it=hosts.begin(); it!=hosts.end(); it++)
	{
		try{
			try{
				switch (it->protocol)
				{
				case IPPROTO_TCP:
					{
					rm::stdset services;
					rq::rpc_portmap_query<rconn::protocol_to_connection<IPPROTO_TCP>::connection_t > pq(it->address, portmap_timeout, ping_timeout);
					pq(services);
					std::copy(services.begin(), services.end(), std::insert_iterator<rm::stdset>(all_services, all_services.begin()));
					}
					break;
				case IPPROTO_UDP:
					{
					rm::stdset services;
					rq::rpc_portmap_query<rconn::protocol_to_connection<IPPROTO_UDP>::connection_t > pq(it->address, portmap_timeout, ping_timeout);
					pq(services);
					std::copy(services.begin(), services.end(), std::insert_iterator<rm::stdset>(all_services, all_services.begin()));
					}
					break;
				default:
					std::ostringstream os;
					os << *it;
					SYSLOG_ERR("rpcm_server unknown protocol: %s", os.str().c_str());
					break;
				}
			}
			catch(re::rpc_error &re) { 
				SYSLOG_ERR("rpcm_server %s", re.what());
			}
		}
		catch(re::rpc_error &re) { 
			SYSLOG_ERR("rpcm_server %s", re.what());
		}
	}
	
	if (monitor.set_services(all_services)==result_failure)
		SYSLOG_RETURN(result_failure, "monitor.set_services failed");
	
	int pgroup;
	if (config.get_pgroup(pgroup)==result_failure)
		SYSLOG_RETURN(result_failure, "monitor.get_pgroup failed");

	if (killpg(pgroup, SIGUSR1)==-1)
		{
		if (errno==ESRCH)
			SYSLOG_ERR("killpg(%d,%d) failed: %m", pgroup, SIGUSR1);
		else
			SYSLOG_RETURN(result_failure, "killpg(%d,%d) failed: %m", pgroup, SIGUSR1);
		}
	return result_success;
}

int main(int argc, char *argv[])
{
	openlog(LOG_IDENT, SYSLOG_OPTION, SYSLOG_FACILITY);

	sigset_t mask;
	if (sigemptyset(&mask)==-1)
		SYSLOG_RETURN(1, "sigemptyset %m");
	if (sigaddset(&mask, SIGUSR1)==-1)
		SYSLOG_RETURN(1, "sigaddset(SIGUSR1) %m");
	if (sigprocmask(SIG_BLOCK, &mask, NULL)==-1)
		SYSLOG_RETURN(1, "sigprocmask %m");

	struct sigaction finish_action;
	finish_action.sa_sigaction = finish_handler;
       	finish_action.sa_mask = mask;
	finish_action.sa_flags = SA_SIGINFO;

	if (sigaction(SIGINT, &finish_action, NULL)==-1)
		SYSLOG_RETURN(1, "sigaction %m");

	while(!finish)
	{
		long delay;
		if (config.get_delay(delay)==result_failure)
			SYSLOG_RETURN(1, "config.get_delay failed");

		if (sleep(delay)==0)
			if (service()==result_failure)
				SYSLOG_RETURN(1, "service failed");
	}

	closelog();
	return 0;
}
